import { getReal, getAngle } from "./common.js";
import Shade from "./shadeLayer/shade.js";

// 鼠标方法 以及 操作图案的核心方法
export default class Behavior extends Shade {
  constructor() {
    super();
    this.stagingMap = {}; // 一些临时性存储的东西
  }

  mousedown(val) {
    this.initProps();

    let x = val.offsetX;
    let y = val.offsetY;
    this.mouseX = x;
    this.mouseY = y;

    if (!this.drag) {
      this.ele = this.judge({ x, y });
      this.fixedCenterPoint(this.ele); // 固定中心点   * 如果不固定中心点，更新画布时矩形位置会发生变化
      this.checkNodeCallback(this.getNode()); // 触发选中节点的回调
      return;
    }

    // 原始的 x、y 数据，用于椭圆缩放时中心点变化计算（因为椭圆在旋转后，缩放时需要计算标记点的真实坐标来确定半径的变化）
    // 但是对于椭圆中心点来说，不需要考虑 旋转 对 圆心 产生的影响，所以在这里重新定义一组 x、y 坐标供椭圆缩放使用
    this.rawDataX = x;
    this.rawDataY = y;

    if (this.type) {
      // 绘图状态
      this.id = new Date().getTime();
      this.ele = null;
      this.drawCanvas = true;
      new Promise((resolve) => {
        this.repaint(resolve);
      }).then(() => {
        this.shadeDown();
      });
    } else {
      // 编辑图形状态
      // 先判断是否点中标记点，如果点中了标记点，拖动时则只改变图案形状，不改变位置
      this.signElement = this.judgeSign({ x, y });
      if (!this.signElement) {
        // 没有点中标记点（可能是拖动图形）
        this.sign = [];
        this.ele = this.judge({ x, y });
        if (this.ele) {
          // 选中了元素
          // 如果选中了某一条连接线，则直接把这条连接线的信息存储起来并且变颜色
          if (
            ["connectCurve", "connectArrowCurveFill"].includes(this.ele.type)
          ) {
            this.connectLineEle = JSON.parse(JSON.stringify(this.ele));
            this.ele = null;
          } else {
            // 在鼠标点击时会根据当前所选中的模型添加高亮，所以此处要保存下来当前图案绘制时的xy
            if (["rect", "image"].includes(this.ele.type)) {
              // 选中了矩形，需要将矩形起点的xy记录下来（其他的图形暂时不需要保存信息）
              this.fixedCenterPoint(this.ele);
            }
            this.isDragElement = true; // 拖拽元素
          }
          this.shadeDown();
        } else {
          // 没有选中元素
          this.clearProps();
          this.repaint();
          this.connectLineEle = null;
          this.mouseX = val.pageX; // 拖拽画布时，要根据浏览器窗口的初始位置计算
          this.mouseY = val.pageY;
          // this.isDragCanvas = true; // 拖拽画布
        }
      } else {
        // 选中了标记点，可能是缩放/旋转或者连线
        this.fixedCenterPoint(this.ele);
        // 点中标记点后，要根据当前标记点判断重新绘制图案时的起点位置
        // 矩形：当前选中点的对位的点
        // 线：当前点击点的另一端
        this.reverse = this.getOtherEnd(this.ele, this.signElement);
        if (this.signElement.signType === "rotate") {
          // 在点击的时候，如果点中了旋转，那么需要记录下初始的弧度（基于被旋转物体的中心）
          if (["rect", "image"].includes(this.ele.type)) {
            this.startRotate = getAngle(
              {
                x: x - this.ele.x - this.ele.width / 2,
                y: this.ele.y + this.ele.height / 2 - y,
              },
              "rotate"
            );
          } else if (this.ele.type === "circular") {
            this.startRotate = getAngle(
              {
                x: x - this.ele.x,
                y: this.ele.y - y,
              },
              "rotate"
            );
          }
        } else if (this.signElement.signType === "zoom") {
          let { realX, realY } = getReal({ x, y, rotate: this.ele.rotate });
          this.mouseX = realX;
          this.mouseY = realY;
        } else if (this.signElement.signType === "connect") {
          this.getAllLinks(); // 计算所有连接点的坐标
          this.drawGetAllLinks(); // 绘制所有图形的连接点
          this.repaintBackboard(); // 更新背景板的图案
        }
        this.isDragElement = true; // 元素形状/位置可被改变
        this.shadeDown();
      }
      this.highlight(this.ele || this.connectLineEle);
    }
    this.repaintBackboard();
  }
  mouseup(val) {
    if (this.type) {
      val.level = this.maxLevel + 1;
      if (this.type === "line") {
        this.drawLine(val, true, false);
      } else if (this.type === "rect") {
        this.drawRect(val, true, false);
      } else if (this.type === "circular") {
        this.drawCircular(val, true, false);
      }
      this.clearProps();
    } else {
      // 当选中了某个图案时鼠标抬起，则会清空当前图案的连接线信息，在下面👇重新计算连接线的信息（防止因为拖拽图形导致的连接线信息改变）
      if (this.ele) {
        let id = this.ele.id;
        // 当鼠标抬起不是因为创建连线而抬起时，要清除多余的连接线，否则 旧的连接线 会遗留至下一次画布刷新时才会被清理
        if (!this.signElement || this.signElement.signType !== "connect") {
          // 清除当前图案所涉及到的连接线的信息（只清除 canvasInfo 中的）
          this.connectInfo.forEach((i) => {
            if (i.startId === id || i.targetId === id) {
              // 这里清空的是图案所接触到的连接线
              this.replaceConnect(i.id);
            }
          });
        }
        // 在编辑/拖拽图形后鼠标抬起时，需要重新绘制当前选中的图案（为了能够重新计算图形变换位置后的属性）
        this.ele = this.reloadElement({ id });
        this.drawBorder(this.ele);
        // 如果当前已经选中了某个图案，则需要重新绘制连接线，因为当前图案原本的连接线信息在上面👆已经被清除了（没有选中图案的时候不需要重新绘制）
        // 用于选中图案时鼠标抬起的场景
        this.repaintConnect(); // 重新绘制连线
      }

      // 连接线的重绘
      if (this.signElement && this.signElement.signType === "connect") {
        // 如果之前使用的连接线，则需要重新绘制当前这条连接线
        this.reloadLink({ val, connectType: this.connectType });
      }
      // else if(this.ele) {
      //   // 如果当前已经选中了某个图案，则需要重新绘制连接线，因为当前图案原本的连接线信息在上面👆已经被清除了（没有选中图案的时候不需要重新绘制）
      //   // 用于选中图案时鼠标抬起的场景
      //   this.repaintConnect();  // 重新绘制连线
      // }
    }
    this.highlight(this.ele || this.connectLineEle);
    this.repaintBackboard();
    this.isDragElement = false; // 点击空白地方，取消对某个图形的选中
    this.linkArr = [];
    this.checkNodeCallback(this.getNode());
  }

  // 重新绘制连接线
  reloadLink({ val, connectType }) {
    // 需要根据鼠标抬起的坐标重新获取所触碰的点
    let targetEle = this.judgeSign({ x: val.offsetX, y: val.offsetY });
    if (targetEle && targetEle.id !== this.ele.id) {
      let res = this.Connect.getThreeCurve({
        startSign: this.signElement,
        targetSign: targetEle,
        type: connectType,
      });
      this.threeCurve(res);
    }
  }

  // 改变图案形状
  changeShape({ val, obj, subX, subY }) {
    if (this.signElement.signType === "rotate") {
      // 旋转，需要计算旋转的弧度
      if (["rect", "image"].includes(this.ele.type)) {
        this.endRotate = getAngle(
          {
            x: val.offsetX - this.ele.x - this.ele.width / 2,
            y: this.ele.y + this.ele.height / 2 - val.offsetY,
          },
          "rotate"
        );
      } else if (this.ele.type === "circular") {
        this.endRotate = getAngle(
          {
            x: val.offsetX - this.ele.x,
            y: this.ele.y - val.offsetY,
          },
          "rotate"
        );
      }
      obj.rotate = obj.rotate + this.endRotate - this.startRotate;
      this.fixedCenterPoint(obj);
    } else if (this.signElement.signType === "zoom") {
      let { realX, realY } = getReal({
        x: val.offsetX,
        y: val.offsetY,
        rotate: this.ele.rotate,
      });
      // 考虑到旋转，所以要用真实坐标计算鼠标移动的距离
      subX = realX - this.mouseX;
      subY = realY - this.mouseY;
      if (["rect", "image"].includes(obj.type)) {
        this.rectShape({ val, obj, subX, subY });
      } else if (obj.type === "circular") {
        this.circularShape({ val, obj, subX, subY });
      }
    }
  }

  // 改变矩形形状
  rectShape({ obj, subX, subY }) {
    if (this.signElement.pos === "1") {
      if (subX + 20 > obj.width) subX = obj.width - 20;
      if (subY + 20 > obj.height) subY = obj.height - 20;
      this.subX = subX;
      this.subY = subY;
      obj.x += subX;
      obj.y += subY;
      obj.width -= subX;
      obj.height -= subY;
    } else if (this.signElement.pos === "2") {
      if (subY + 20 > obj.height) subY = obj.height - 20;
      this.subX = 0;
      this.subY = subY;
      obj.y += subY;
      obj.width += subX;
      obj.height -= subY;
    } else if (this.signElement.pos === "3") {
      obj.width += subX;
      obj.height += subY;
    } else if (this.signElement.pos === "4") {
      if (subX + 20 > obj.width) subX = obj.width - 20;
      this.subX = subX;
      this.subY = 0;
      obj.x += subX;
      obj.width -= subX;
      obj.height += subY;
    }
    // 注意：这里在缩放的时候不能让其把宽高变成负数，否则相当于图案进行了旋转
    if (obj.height < 20 || obj.width < 20) {
      if (obj.height < 20) {
        obj.height = 20;
      }
      if (obj.width < 20) {
        obj.width = 20;
      }
    }
  }

  // 改变圆形形状
  circularShape({ val, obj, subX, subY }) {
    obj.x += (val.offsetX - this.rawDataX) / 2;
    obj.y += (val.offsetY - this.rawDataY) / 2;
    if (this.signElement.pos === "1") {
      obj.radiusX -= subX / 2;
      obj.radiusY -= subY / 2;
    } else if (this.signElement.pos === "2") {
      obj.radiusX += subX / 2;
      obj.radiusY -= subY / 2;
    } else if (this.signElement.pos === "3") {
      obj.radiusX += subX / 2;
      obj.radiusY += subY / 2;
    } else if (this.signElement.pos === "4") {
      obj.radiusX -= subX / 2;
      obj.radiusY += subY / 2;
    }
    // 注意：这里在缩放的时候不能让其把宽高变成负数，否则相当于图案进行了旋转
    if (obj.radiusX < 10) {
      obj.radiusX = 10;
    }
    if (obj.radiusY < 10) {
      obj.radiusY = 10;
    }
  }

  // 根据鼠标位置判断是否高亮
  // ele: 要高亮的元素
  highlight(ele) {
    // 如果存在高亮元素，会先取消高亮状态
    if (this.highlightEle) {
      // 如果已经存在高亮图案，则先将此图案的高亮状态取消
      let target = this.canvasInfoMap.get(this.highlightEle.id);
      if (target) {
        target.fillColor = this.stagingMap.fillColor;
        target.lineWidth = this.stagingMap.lineWidth;
        this.stagingMap = {};
        this.highlightEle = "";
        this.replaceEle(target);
        this.empty(); // 清空一次画布来清空高亮图形
        setTimeout(() => {
          if (!ele) {
            this.repaint(); // 此时为点击空白且有元素取消高亮的情况，通过微任务触发画布重绘（如果单纯点击空白处则无需重绘）
          }
        }, 0);
      }
    }
    if (ele) {
      // 如果存在高亮元素，则需要重新获取该元素的状态（因为在上面👆更新了状态但是没有响应）
      let newEle = this.canvasInfoMap.get(ele.id) || {};
      if (!ele.unLight) {
        this.stagingMap.fillColor = newEle.fillColor;
        this.stagingMap.lineWidth = newEle.lineWidth;
        // 将自定义高亮信息填入
        newEle.fillColor = this.highlightColor;
        this.highlightEle = newEle;
        this.ele = newEle;
      }
      this.dragElement(JSON.parse(JSON.stringify(newEle)));
    }
  }
}
